/*
提供琐碎的工能
1 简单的宏打印日志功能
*/

#pragma once
#include <time.h>
#include <jsoncpp/json/json.h>
#include <string>
#include <memory>
#include <cstdio>
#include <string>
#include <filesystem>
#include <iostream>
#include <chrono>
#include <random>
#include <sstream>
#include <unistd.h>
#include <atomic>
#include <iomanip>
#include <mutex>
#define DUG 0
#define INF 1
#define ERR 2
#define DEF 30

/*
宏定义的本质特性
‌预处理器机制‌：

宏是由预处理器处理的文本替换机制
预处理器在编译器解析命名空间等 C++ 语法‌之前‌执行
宏定义不受命名空间作用域限制
‌全局作用域‌：

无论宏定义在代码的哪个位置（包括命名空间内部）
宏展开时都会在‌全局作用域‌生效
使用宏时不需要也不应该添加命名空间限定符
错误原因分析
当您使用 LOGUnit::DLOG(...) 时：

预处理器首先展开 DLOG 宏 ➔ LOG(DUG, ...)
接着展开 LOG 宏 ➔ do { ... } while(0) 代码块
此时语法变为：LOGUnit::do { ... } while(0)
编译器看到 LOGUnit:: 后接代码块 ➔ 语法错误（期待类名/函数名）

*/
#define LOG(level, fmt, ...)                                                        \
    {                                                                               \
        if ((level) > DEF)                                                          \
        {                                                                           \
            time_t tm = time(nullptr);                                              \
            struct tm *t = localtime(&tm);                                          \
            char time_buff[30];                                                     \
            snprintf(time_buff, sizeof(time_buff), "%04d-%02d-%02d %02d:%02d:%02d", \
                     t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,                  \
                     t->tm_hour, t->tm_min, t->tm_sec);                             \
            printf("[%s][%s:%d] " fmt ,                                         \
                   time_buff, __FILE__, __LINE__, ##__VA_ARGS__);                   \
        }                                                                           \
    }

#define DLOG(fmt, ...) LOG(DUG, fmt, ##__VA_ARGS__)
#define ILOG(fmt, ...) LOG(INF, fmt, ##__VA_ARGS__)
#define ELOG(fmt, ...) LOG(ERR, fmt, ##__VA_ARGS__)

// namespace LOGUnit

class JSON
{
public:
    static bool Serialize(Json::Value &root, std::string &body)
    {

        // 序列化--把stu对象内部的key——value序列化成一个字符串然后输出到一个输出流对象
        Json::StreamWriterBuilder Bst;
        // StreamWriter 是一个抽象的基类，
        // 要定义一个工厂类对象去生产(new)一个StreamWriter的子类对象
        // 使用完要释放，避免忘记释放使用指针指针管理
        std::shared_ptr<Json::StreamWriter> sw(Bst.newStreamWriter());
        std::stringstream ss;
        // 把序列化的字符串输出到字符串输出流对象中
        int ret = sw->write(root, &ss);
        if (ret != 0)
            return false;
        body = ss.str();
        return true;
    }

    // json字符串反序列化到Value对象中以key--value存储
    static bool UnSerialize(Json::Value &root, std::string &body)
    {
        DLOG("接收的正文:\n%s", body.c_str());
        Json::CharReaderBuilder Cb;
        std::shared_ptr<Json::CharReader> Cr(Cb.newCharReader());
        std::string strerr;
        bool ret = Cr->parse(body.c_str(), body.c_str() + body.size(), &root, &strerr);
        //std::cout << strerr << std::endl;
        return ret;
    }
};

class UUID
{
public:
    static std::mutex _mutex;
    static std::string uuid()
    {
        // 加锁--Rpc客户端可能会有多个线程发送Rpc请求
        std::unique_lock<std::mutex> lock(_mutex);
        //  1 构造机器随机数对象--硬件随机数不是伪随机数
        std::random_device rdv;
        // 2 构建一个伪随机数对象，用硬件随机数作为种子，
        // 这样每次生成的伪随机数就可能不一样了，以时间为种子的话，一秒之内生成的伪代随机数是一样的
        // 因为种子一样的话每一次重复生成随机数的话生成的随机数是和上一批随机数一样的
        // 或者每次程序运行时产生的随机数和上一次产生的一样
        std::mt19937 generation(rdv()); // rdv()重载（）返回硬件随机数

        // 把产生的伪随机数的数值范围控制一下
        std::uniform_int_distribution<int> distribution(0, 255);
        std::stringstream ss;
        for (int i = 0; i < 8; i++)
        {
            if (i == 4 || i == 6)
                ss << "-";
            // 传入伪随机数对象，每次运行改程序伪随机对象的种子都不一样所以生成的伪随机数也不一样
            ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generation);
        }
        ss << "-";

        // 定义一个静态局部原子对象--只会线程安全的初始化一次
        static std::atomic<int> seq(1);
        // 给原子对象的值加1，原子操作线程安全
        size_t cur = seq.fetch_add(1);
        // 定义一个长整形无符号八字节数据
        // 把每个字节拿出来转成16进制
        for (int i = 7; i >= 0; i--)
        {
            // 位数不一样的数据做运算发送截断
            if (i == 5)
                ss << "-";
            ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> i * 8) & 0xff);
        }
        // //定义一个原子对象初始化值为1
        // std::atomic<int> seq(1);
        // //原子对象值加一个数返回加之前的值，整个操作都是原子的加锁的
        // int cur = seq.fetch_add(2);
        // //原子对象值加加反回加之前的值，整个操作都是原子的加锁的
        // seq++;
        // std::cout << cur << std::endl;
        // //获取当前原子对象的值--也是原子的
        // cur = seq.load();
        // std::cout << cur << std::endl;

        // std::cout << seq.load() << std::endl;
        return ss.str();
    }
  
};
std::mutex UUID::_mutex;